﻿using System;
using System.Collections.Concurrent;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;


namespace BLL.Wapper
{
    public class WebSocketWapper
    {
        private static ConcurrentDictionary<string, System.Net.WebSockets.WebSocket> _sockets = new ConcurrentDictionary<string, System.Net.WebSockets.WebSocket>();

        private readonly RequestDelegate _next;

        public WebSocketWapper(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            if (!context.WebSockets.IsWebSocketRequest)
            {
                await _next.Invoke(context);
                return;
            }
            System.Net.WebSockets.WebSocket dummy;

            CancellationToken ct = context.RequestAborted;
            WebSocket currentSocket = await context.WebSockets.AcceptWebSocketAsync();
            //string socketId = Guid.NewGuid().ToString();
            string accessToken = context.Request.Query["sid"].ToString();
            BLL.Manage.UserManage userManage = new Manage.UserManage();
            Model.Basics.UserModel.UserTokenModel user = userManage.GetUserInfoByToken(accessToken);
            if (user == null)
            {
                await currentSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", ct);
                currentSocket.Dispose();
            }
            else
            {
                string socketId = user.MemberId;
                if (!_sockets.ContainsKey(socketId))
                {
                    _sockets.TryAdd(socketId, currentSocket);
                }
                else
                {
                    _sockets[socketId] = currentSocket;
                }
                //_sockets.TryRemove(socketId, out dummy);
                //_sockets.TryAdd(socketId, currentSocket);

                while (true)
                {
                    if (ct.IsCancellationRequested)
                    {
                        _sockets.TryRemove(socketId, out dummy);
                        break;
                    }


                    string response = await ReceiveStringAsync(currentSocket, ct);
                    Model.Basics.UserModel.SocketMessageTamplate msg = JsonConvert.DeserializeObject<Model.Basics.UserModel.SocketMessageTamplate>(response);

                    if (string.IsNullOrEmpty(response))
                    {
                        if (currentSocket.State != WebSocketState.Open)
                        {
                            break;
                        }

                        continue;
                    }

                    foreach (var socket in _sockets)
                    {
                        bool flag = socket.Key == msg.ReceiverID;
                        if (flag)
                        {
                            if (socket.Value.State != WebSocketState.Open)
                            {
                                continue;
                            }
                            // switch (msg.SocketMessageType)
                            // {
                            //     case 1:
                            //         var friendFlag = new BLL.Manage.FriendsManage().IsFriends(msg.ReceiverID);
                            //         // 一对一好友聊天消息
                            //         if (friendFlag)
                            //             await SendStringAsync(socket.Value, JsonConvert.SerializeObject(msg), ct);
                            //         break;
                            //     default:
                            //         break;
                            // }
                        }
                        else
                        {
                            continue;
                        }
                        //if (socket.Key == msg.ReceiverID || socket.Key == socketId)
                        //{
                        //    await SendStringAsync(socket.Value, JsonConvert.SerializeObject(msg), ct);
                        //}
                    }
                }
                //_sockets.TryRemove(socketId, out dummy);
                await currentSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", ct);
                currentSocket.Dispose();
            }

        }

        public static Task SendStringAsync(System.Net.WebSockets.WebSocket socket, string data, CancellationToken ct = default(CancellationToken))
        {
            var buffer = Encoding.UTF8.GetBytes(data);
            var segment = new ArraySegment<byte>(buffer);
            return socket.SendAsync(segment, WebSocketMessageType.Text, true, ct);
        }

        public static Task SendObjectAsync(string socketId, object data, CancellationToken ct = default(CancellationToken))
        {
            if (_sockets.ContainsKey(socketId))
            {
                var find = _sockets[socketId];
                if (find.State == WebSocketState.Open)
                {
                    var setting = new JsonSerializerSettings
                    {
                        ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
                    };
                    string sendData = JsonConvert.SerializeObject(data, Formatting.None, setting);
                    var buffer = Encoding.UTF8.GetBytes(sendData);
                    var segment = new ArraySegment<byte>(buffer);
                    return find.SendAsync(segment, WebSocketMessageType.Text, true, ct);
                }
            }
            return null;
        }

        private static async Task<string> ReceiveStringAsync(System.Net.WebSockets.WebSocket socket, CancellationToken ct = default(CancellationToken))
        {
            var buffer = new ArraySegment<byte>(new byte[8192]);
            using (var ms = new MemoryStream())
            {
                WebSocketReceiveResult result;
                do
                {
                    ct.ThrowIfCancellationRequested();

                    result = await socket.ReceiveAsync(buffer, ct);
                    ms.Write(buffer.Array, buffer.Offset, result.Count);
                }
                while (!result.EndOfMessage);

                ms.Seek(0, SeekOrigin.Begin);
                if (result.MessageType != WebSocketMessageType.Text)
                {
                    return null;
                }

                using (var reader = new StreamReader(ms, Encoding.UTF8))
                {
                    return await reader.ReadToEndAsync();
                }
            }
        }
    }
}
